<?php
// +----------------------------------------------------------------------
// | 订单文件列表 自定义服务
// +----------------------------------------------------------------------
// | 主要服务于Version数据处理
// +----------------------------------------------------------------------
// | 错误编码头 213xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\OrderModel;
use common\model\OrderTaskFileListModel;
use common\model\OrderTaskListModel;
use common\model\OrderTaskUpstreamVersionModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\TaskModel;
use common\model\VersionModel;
use support\bootstrap\Redis;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;

class OrderTaskFileListService
{
    use SingletonTrait;

    /** @var OrderTaskFileListModel */
    private $orderTaskFileListModel;
    private $orderTaskListModel;

    public function __construct()
    {
        $this->orderTaskFileListModel = model(OrderTaskFileListModel::class);
        $this->orderTaskListModel = model(OrderTaskListModel::class);
    }

    public function select($param)
    {
        $fields = $param['fields'] ?? 'order_task_file_list.id,order_task_file_list.version_id,order_task_file_list.task_id,order_task_file_list.downstream_task_id';
        $order = $param['order'] ?? 'order_task_file_list.id DESC';
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $filter = $param['filter'] ?? [];

        $count = $this->orderTaskFileListModel
            ->join('`order` ON `order`.id = order_task_file_list.order_id')
            ->join('project ON project.id = order_task_file_list.from_project_id')
            ->join('task ON task.id = order_task_file_list.task_id')
            ->join('version ON version.id = order_task_file_list.version_id', 'LEFT')
            ->join('entity ON entity.id = order_task_file_list.entity_id')
            ->where($filter)
            ->count();
        $fields = explode(',', $fields);
        $fields = build_complex_query_fields($fields, '__');
        $rows = $this->orderTaskFileListModel
            ->join('`order` ON `order`.id = order_task_file_list.order_id')
            ->join('project ON project.id = order_task_file_list.from_project_id')
            ->join('task ON task.id = order_task_file_list.task_id')
            ->join('version ON version.id = order_task_file_list.version_id', 'LEFT')
            ->join('entity ON entity.id = order_task_file_list.entity_id')
            ->where($filter)
            ->field($fields)
            ->order($order)
            ->page($page[0], $page[1])
            ->select();

        foreach ($rows as &$row) {
            $row = separate_complex_query_data($row, '__');
        }
        return ['total' => $count, 'rows' => $rows];
    }

    /**
     * 查询上传文件列表
     * @param array $param
     * @param int $userId
     * @param int $tenantId
     * @param array $taskFilter
     * @param array $versionFilter
     * @return array
     * @throws \Exception
     */
    public function getUploadList(array $param, int $userId, int $tenantId, $taskFilter = [], $versionFilter = [])
    {
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $where = [
            'version.is_uploaded' => 'no',
        ];
        if (!empty($param['filter']['version.project_id'])) {
            $where['version.project_id'] = $param['filter']['version.project_id'];
        } else {
            $projectList = ProjectService::getInstance()->getUserProjectList($userId, ['param' => ['project_status' => 'inprogress']]);
            $projectIds = array_column(array_column($projectList['rows'], 'project'), 'id');
            if (empty($projectIds)) {
                return [];
            }
            $where['version.project_id'] = ['IN', $projectIds];
        }
        $maxNeedUploadQuery = model(OrderTaskUpstreamVersionModel::class)->alias('ov')
            ->join('task on task.id = ov.task_id')
            ->join('entity on entity.id = task.entity_id')
            ->join('version on ov.version_id = version.id')
            ->where(['ov.from_tenant_id' => $tenantId, 'task.executor_type' => ['IN', [TaskModel::EXECUTOR_TYPE_DEFAULT, TaskModel::EXECUTOR_TYPE_INSIDE]]])
            ->where($taskFilter)
            ->field('max(ov.version_id) as version_id,ov.task_id')
            ->group('ov.task_id');

        $subSql = $maxNeedUploadQuery->buildSql();

        if ($page[0] == 1) {
            $res['total'] = model(VersionModel::class)
                ->join('join ' . $subSql . ' vm on vm.version_id = version.id')
                ->where($where)
                ->where($versionFilter)
                ->count();
        } else {
            unset($res['total']);
        }

        $uploadRes = model(VersionModel::class)
            ->join('join ' . $subSql . ' vm on vm.version_id = version.id')
            ->where($where)
            ->where($versionFilter)
            ->field('version.*')
            ->page($page[0], $page[1])
            ->order('version.id desc')
            ->select();
        if (empty($uploadRes)) {
            return $res;
        }
        $taskIdList = array_column($uploadRes, 'link_id');
        $projectIdList = array_column($uploadRes, 'project_id');
        $versionListMap = array_column($uploadRes, null, 'id');
        $projectList = model(ProjectModel::class)->where(['id' => ['IN', implode(',', $projectIdList)]])
            ->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,is_pack_upload,belong_project_id')
            ->select();
        $projectList = array_column($projectList, null, 'id');
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        //工序--环节
        $stepCategoryIdList = array_column($taskList, 'step_category_id');
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $statusIdList = array_column($taskList, 'task_status_id', 'task_status_id');
        $entityIdList = array_column($taskList, 'entity_id', 'entity_id');
        $statusList = model(StatusModel::class)->where(['id' => ['IN', implode(',', $statusIdList)]])->select();
        $statusList = array_column($statusList, null, 'id');

        $entityList = model(EntityModel::class)->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,initial_episode_code,showings_number")
            ->select();
        $entityList = array_column($entityList, null, 'id');

        $uploadedVersionIdList = model(VersionModel::class)
            ->where(['module_id' => module_by_code('task')['id'], 'link_id' => ['IN', implode(',', $taskIdList)], 'is_uploaded' => 'yes'])
            ->field('max(id) as version_id,link_id')
            ->group('link_id')
            ->select();
        $uploadedVersionIdMap = [];
        if (!empty($uploadedVersionIdList)) {
            $uploadedVersionIdMap = array_column($uploadedVersionIdList, 'version_id', 'link_id');
            $uploadedVersionList = model(VersionModel::class)->where(['id' => ['IN', implode(',', array_column($uploadedVersionIdList, 'version_id', 'version_id'))]])->select();
            $versionListMap += array_column($uploadedVersionList, null, 'id');
        }

        $uploadRes = array_column($uploadRes, null, 'link_id');
        foreach ($taskList as &$task) {
            $uploadItem = $uploadRes[$task['id']];
            $task['downloaded'] = false;
            $task['has_update'] = true;
            $task['uploaded'] = false;

            $task['module']['code'] = module_by_id($task['entity_module_id'])['code'];
            $task['step_category'] = $stepCategoryList[$task['step_category_id']] ?? null;

            $task['version'] = $task['latest_version'] = $versionListMap[$uploadItem['id']] ?? null;
            $task['entity'] = $entityList[$task['entity_id']] ?? null;
            $task['project'] = $projectList[$uploadItem['project_id']] ?? null;
            $task['task_status'] = $statusList[$task['task_status_id']] ?? null;


            if (!empty($uploadedVersionIdMap[$task['id']])) {
                $task['current_version'] = $versionList[$uploadedVersionIdMap[$task['id']]] ?? null;
            } else {
                $task['current_version'] = null;
            }
        }

        $res['rows'] = $taskList;
        return $res;

    }

    /**
     * 查询下载文件列表
     * @param array $param
     * @param int $userId
     * @param int $tenantId
     * @param $taskFilter
     * @param $downloadFilter
     * @return array
     * @throws \Exception
     */
    public function getDownloadList(array $param, int $userId, int $tenantId, $taskFilter, $downloadFilter)
    {
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];

        $where = [
            'ov2.is_download' => 'no',
        ];
        if (!empty($param['filter']['project_id'])) {
            $where['otfl2.to_project_id'] = $param['filter']['project_id'];
        } else {
            $projectList = ProjectService::getInstance()->getUserProjectList($userId, ['param' => ['project_status' => 'inprogress']]);
            $projectIds = array_column(array_column($projectList['rows'], 'project'), 'id');
            if (empty($projectIds)) {
                return [];
            }
            $where['otfl2.to_project_id'] = ['IN', $projectIds];
        }
        $maxNeedDownloadQuery = model(OrderTaskUpstreamVersionModel::class)->alias('ov')
            ->join('task on ov.task_id = task.id')
            ->join('entity on entity.id = task.entity_id')
            ->join('version on ov.version_id = version.id')
            ->where(['ov.to_tenant_id' => $tenantId, 'version.is_uploaded' => 'yes'])
            ->where($taskFilter)
            ->field('max(ov.id) as id,ov.task_id')
            ->group('ov.task_id');

        $subSql = $maxNeedDownloadQuery->buildSql();

        if ($page[0] == 1) {
            $res['total'] = model(OrderTaskUpstreamVersionModel::class)
                ->alias('ov2')
                ->join('join ' . $subSql . ' vm on vm.id = ov2.id')
                ->join('order_task_file_list otfl2 on otfl2.task_id = ov2.task_id and otfl2.to_tenant_id = ov2.to_tenant_id')
                ->where($where)
                ->where($downloadFilter)
                ->field('ov2.*,otfl2.to_project_id,otfl2.entity_id')
                ->count("distinct ov2.id");
        } else {
            unset($res['total']);
        }

        $downloadRes = model(OrderTaskUpstreamVersionModel::class)
            ->alias('ov2')
            ->join('join ' . $subSql . ' vm on vm.id = ov2.id')
            ->join('order_task_file_list otfl2 on otfl2.task_id = ov2.task_id and otfl2.to_tenant_id = ov2.to_tenant_id')
            ->where($where)
            ->where($downloadFilter)
            ->field('ov2.*,otfl2.to_project_id,otfl2.entity_id,otfl2.step_category_id')
            ->page($page[0], $page[1])
            ->order('ov2.id desc')
            ->group('ov2.id')
            ->select();
        if (empty($downloadRes)) {
            return $res;
        }
        //工序--环节
        $stepCategoryIdList = array_column($downloadRes, 'step_category_id');
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $taskIdList = array_column($downloadRes, 'task_id');
        $projectIdList = array_column($downloadRes, 'to_project_id');
        $entityIdList = array_column($downloadRes, 'entity_id', 'entity_id');
        $versionIdList = array_column($downloadRes, 'version_id', 'version_id');
        $downloadRes = array_column($downloadRes, null, 'task_id');
        $entityList = model(EntityModel::class)->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,initial_episode_code,showings_number")
            ->select();
        $entityList = array_column($entityList, null, 'id');
        $projectList = model(ProjectModel::class)->where(['id' => ['IN', implode(',', $projectIdList)]])
            ->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,belong_project_id,is_pack_upload')
            ->select();
        $projectList = array_column($projectList, null, 'id');
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        $statusIdList = [];
        $executeTenantIdList = [];
        foreach ($taskList as $task) {
            if ($task['task_status_id'] > 0 && !in_array($task['task_status_id'], $statusIdList)) {
                $statusIdList[] = $task['task_status_id'];
            }
            if ($task['executor_tenant_id'] > 0 && !in_array($task['executor_tenant_id'], $executeTenantIdList)) {
                $executeTenantIdList[] = $task['executor_tenant_id'];
            }
        }
        $tenantList = [];
        if (!empty($executeTenantIdList)) {
            $tenantList = teamones_request('teamones-im', 'tenant/select', [
                'param' => [
                    'filter' => [
                        'tenant.id' => [
                            '-in', implode(',', $executeTenantIdList),
                        ],
                    ],
                ],
            ])['data']['rows'];
            $tenantList = array_column($tenantList, null, 'id');
        }

        $statusList = model(StatusModel::class)->where(['id' => ['IN', implode(',', $statusIdList)]])->select();
        $statusList = array_column($statusList, null, 'id');
        // 已下载版本
        $downloadedVersionIdList = model(OrderTaskUpstreamVersionModel::class)
            ->where(['to_tenant_id' => $tenantId, 'task_id' => ['IN', implode(',', $taskIdList)], 'is_download' => 'yes'])
            ->field('max(version_id) as version_id,task_id')
            ->group('task_id')
            ->select();
        $downloadedVersionMap = [];
        if (!empty($downloadedVersionIdList)) {
            $downloadedVersionMap = array_column($downloadedVersionIdList, 'version_id', 'task_id');
            $versionIdList += array_column($downloadedVersionIdList, 'version_id', 'version_id');
        }
        $versionList = model(VersionModel::class)->where(['id' => ['IN', implode(',', $versionIdList)]])->select();
        $versionList = array_column($versionList, null, 'id');

        foreach ($taskList as &$task) {
            $task['downloaded'] = false;
            $task['has_update'] = true;
            $task['uploaded'] = true;
            $downloadResItem = $downloadRes[$task['id']];
            $task['order_task_upstream_version'] =  &$downloadRes[$task['id']];

            $task['module']['code'] = module_by_id($task['entity_module_id'])['code'];
            $task['step_category'] = $stepCategoryList[$downloadResItem['step_category_id']] ?? null;

            $task['project'] = $projectList[$downloadResItem['to_project_id']] ?? null;
            $task['entity'] = $entityList[$downloadResItem['entity_id']] ?? null;
            $task['latest_version'] = $task['version'] = $versionList[$downloadResItem['version_id']] ?? null;
            $task['task_status'] = $statusList[$task['task_status_id']] ?? null;
            if (!empty($downloadedVersionMap[$task['id']])) {
                $task['current_version'] = $versionList[$downloadedVersionMap[$task['id']]] ?? null;
            } else {
                $task['current_version'] = null;
            }
            $task['executor_tenant'] = $tenantList[$task['executor_tenant_id']] ?? null;
        }
        $res['rows'] = $taskList;
        return $res;
    }

    /**
     * 标记下游任务文件已经解压成功
     * @param int $downstreamTaskId
     * @throws \Exception
     */
    public function markDownstreamTaskUnzipped(int $downstreamTaskId)
    {
        $orderTaskFileList = $this->orderTaskFileListModel
            ->where([
                'downstream_task_id' => $downstreamTaskId,
            ])
            ->field('id')
            ->select();
        if (count($orderTaskFileList) > 0) {
            $this->orderTaskFileListModel->modifyItem([
                'id' => ['IN', implode(',', array_column($orderTaskFileList, 'id'))],
                'is_unzip' => 'yes',
            ]);
        }
    }

    /**
     * 标记版本正在上传
     * @param $versionId
     * @param $tenantId
     * @return bool
     */
    public function markVersionUploading($versionId, $tenantId)
    {
        $ttl = 60 * 60; // 如果一个小时之后还未完成上传 那么该下载任务就可以再次被获得

        // 检查是否在进行中
        if (!empty(Redis::get("version_uploading_$tenantId:$versionId"))) {
            throw_strack_exception('version is uploading', ErrorCode::VERSION_IS_UPLOADING);
        }
        return Redis::setEx("version_uploading_$tenantId:$versionId", $ttl, $versionId);
    }

    /**
     * 标记版本已经上传
     * @param array $data
     * @param int $tenantId
     * @param int $userId
     * @return array
     * @throws Exception
     */
    public function markVersionUploaded(array $data, int $tenantId, int $userId)
    {
        $commonService = CommonService::instance('version');
        $res = $commonService->update($data, 'version');
        $this->delUploadingItem($tenantId, $res['id']);
        $version = model(VersionModel::class)->find($data['id']);
        $relatedOrderListFilter = [
            'task_id' => $version['link_id'],
            'from_tenant_id' => $tenantId,
        ];
        $relatedOrderList = model(OrderTaskFileListModel::class)
            ->where($relatedOrderListFilter)
            ->field('order_id')
            ->group('order_id')
            ->select();

        if (!empty($relatedOrderList)) {
            $relatedOrderIdList = array_column($relatedOrderList, 'order_id');
            $order = model(OrderModel::class)->find($relatedOrderIdList[0]);
            $eventData = [
                'role' => $tenantId == $order['to_tenant_id'] ? 'partyB' : 'partyA',
                'related_order_id' => array_column($relatedOrderList, 'order_id'),
            ];
            try {
                MessageService::getInstance()->notifyUserEvent($userId, 'order_file_uploaded_success', $eventData);
            } catch (Throwable $e) {
                trace(format_exception_info($e), 'ERR');
            }
        }
        return $res;
    }

    /**
     * 删除正在上传标记
     * @param $tenantId
     * @param $id
     * @return int
     */
    private function delUploadingItem($tenantId, $id)
    {
        return Redis::del("version_uploading_$tenantId:$id");
    }

    /**
     * 甲方下载列表
     * @param $param
     * @return array
     */
    public function getPartyADownloadList($param)
    {
        $projectId = $param['project_id'] ?? [];
        $page = get_default_page($param);
        if (empty($projectId)) {
            $projectList = $this->getProjectList(request()->getTenantId(), ProjectModel::TYPE_INSOURCE);
            $projectId = array_column($projectList, 'id');
        }

        $where = $this->parseListFilter($param['filter'] ?? [], true, true);
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        if (empty($projectId)) {
            return $res;
        }
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => ['IN', $projectId],
        ];
        $res['total'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.to_project_id = `order`.project_id and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->count();

        $fields = [
            'e.id',
            'e.name',
            'e.code',
            'e.initial_episode_code',
            'e.showings_number',
            'e.module_id',
            'e.project_id',
            'e.json',
            'task.id',
            'task.tenant_id',
            'task.name',
            'task.code',
            'task.step_category_id',
            'task.step_id',
            'task.json',
            'v.id',
            'v.number',
            'v.link_id',
            'v.module_id',
            'v.description',
            'v.is_pass',
            'v.is_publish',
            'v.json',
            'v.param',
            'v.created_by',
            'v.created',
            'v.updated',
            'v.is_uploaded',
            'v.pack_name',
            'v.pack_pass',
            'v.cos_upload_name',
            'v.published',
            'v.storage_type',
            'otuv.id',
            'otuv.from_tenant_id',
            'otuv.is_download',
            'otuv.version_id',
            'otuv.task_id',
        ];

        $res['rows'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.to_project_id = `order`.project_id and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->field(build_complex_query_fields($fields, '__'))
            ->page($page[0], $page[1])
            ->select();
        $stepList = StepService::getInstance()->getList([], 'id,name,code,color');
        $stepList = array_column($stepList, null, 'id');
        $stepCategoryList = StepCategoryService::getInstance()->getList([], 'id,name,code,color');
        $stepCategoryList = array_column($stepCategoryList, null, 'id');
        $statusList = StatusService::getInstance()->getList([], 'id,name,color,code');
        $statusList = array_column($statusList, null, 'id');
        $tenantIdList = [];
        $projectIdList = [];
        foreach ($res['rows'] as &$row) {
            $row = separate_complex_query_data($row, '__');
            $row['module'] = module_by_id($row['e']['module_id']);
            $moduleCode = $row['module']['code'] ?? 'entity';
            request()->getModuleCode($moduleCode);
            $row['entity'] = CommonService::getFormatAttrData(model(EntityModel::class), $row['e']);
            request()->getModuleCode('task');
            $row['task'] = CommonService::getFormatAttrData(model(TaskModel::class), $row['task']);
            $row['task_status'] = null;
            if (!empty($row['task']['json']['task_status'])) {
                $row['task_status'] = $statusList[$row['task']['json']['task_status']] ?? null;
            }
            $row['step'] = $stepList[$row['task']['step_id']] ?? null;
            $row['step_category'] = $stepCategoryList[$row['task']['step_category_id']] ?? null;
            $row['max_version_id'] = $row['v']['id'];
            $row['max_version'] = $row['version'] = CommonService::getFormatAttrData(model(VersionModel::class), $row['v']);
            if (!in_array($row['task']['tenant_id'], $tenantIdList)) {
                $tenantIdList[] = $row['task']['tenant_id'];
            }
            $projectIdList[$row['entity']['project_id']] = $row['entity']['project_id'];
            unset($row['v'], $row['e']);
        }
        $tenantList = [];
        if (!empty($tenantIdList)) {
            $tenantList = teamones_request('teamones-im', 'tenant/select', [
                'param' => [
                    'filter' => [
                        'tenant.id' => ['-in', implode(',', $tenantIdList)],
                    ],
                    'fields' => "tenant.id,tenant.name",
                ],
            ]);
            $tenantList = array_column($tenantList['data']['rows'], null, 'id');
        }
        $projectList = [];
        if (!empty($projectIdList)) {
            $projectList = model(ProjectModel::class)
                ->where(['id' => ['IN', implode(',', $projectIdList)]])
                ->field([
                    'id',
                    'code',
                    'work_dir',
                    'work_dir_prefix',
                    'engine_dir_prefix',
                    'is_pack_upload',
                    'belong_project_id',
                ])
                ->select();
            $projectList = array_column($projectList, null, 'id');
        }
        unset($row);
        foreach ($res['rows'] as &$row) {
            $row['tenant'] = null;
            if (!empty($row['task']['tenant_id'])) {
                $row['tenant'] = $tenantList[$row['task']['tenant_id']] ?? null;
            }
            $row['project'] = null;
            if (!empty($projectList[$row['entity']['project_id']])) {
                $row['project'] = $projectList[$row['entity']['project_id']];
            }
        }
        return $res;
    }

    /**
     * 下载上传 筛选器解析
     * @param $filter
     * @param bool $isPartyA
     * @param bool $isDownload
     * @return array
     */
    private function parseListFilter($filter, bool $isPartyA, bool $isDownload)
    {
        $episodeCode = $filter['episode_code'] ?? '';
        $supplierId = $filter['supplier_id'] ?? [];
        $customerId = $filter['customer_id'] ?? [];
        $stepIdList = $filter['step_id'] ?? [];
        $statusCodeList = $filter['status_code'] ?? [];
        $fileStatus = $filter['file_status'] ?? '';
        $taskName = $filter['task_name'] ?? '';
        $moduleCode = $filter['module_code'] ?? '';
        $category = $filter['category'] ?? '';
        $downloadingFilter = $filter['downloading_filter'] ?? 'default';
        $uploadingFilter = $filter['uploading_filter'] ?? 'default';
        $downloadingFilter = trim($downloadingFilter);
        $uploadingFilter = trim($uploadingFilter);
        $where = [];
        $entityFilterKey = '';
        if (
            ($isPartyA && $isDownload)
            ||
            (!$isPartyA && !$isDownload)
        ) {
            $entityFilterKey = 'e';
        } else if (
            ($isPartyA && !$isDownload)
            ||
            (!$isPartyA && $isDownload)
        ) {
            $entityFilterKey = 'entity';
        }
        if (!empty($episodeCode)) {
            $where['e.initial_episode_code'] = $episodeCode;
        }
        if (!empty($supplierId)) {
            $where['`order`.to_tenant_id'] = ['IN', $supplierId];
        }
        if (!empty($customerId)) {
            $where['`order`.from_tenant_id'] = ['IN', $customerId];
        }
        if (!empty($stepIdList)) {
            $where['task.step_id'] = ['IN', $stepIdList];
        }
        if (!empty($statusCodeList)) {
            $statusList = StatusService::getInstance()->getList(['code' => ['IN', $statusCodeList]], 'id');
            if (!empty($statusList)) {
                $where["task.json->>'$.task_status'"] = ['IN', array_column($statusList, 'id')];
            }
        }
        if (!empty($fileStatus)) {
            switch ($fileStatus) {
                case 'wait_publish':
                    $where[] = [
                        'v.id' => ['EXP', 'IS NULL'],
                    ];
                    break;
                case 'wait_upload':
                    $where[] = [
                        'v.is_uploaded' => 'no',
                        'v.is_publish' => 'yes',
                    ];
                    break;
                case 'wait_download':
                    $where[] = [
                        'v.is_uploaded' => 'yes',
                        'otuv.is_download' => 'no',
                    ];
                    break;
                case 'downloaded':
                    $where[] = [
                        'v.is_uploaded' => 'yes',
                        'otuv.is_download' => 'yes',
                    ];
                    break;
            }
        }

        if (!empty($taskName)) {
            $where[] = [
                "{$entityFilterKey}.name" => ['LIKE', $taskName],
            ];
        }
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $shotModuleIdList = [
            module_by_code('shot')['id'],
            module_by_code('sequence')['id'],
        ];
        if (!empty($moduleCode)) {
            if ($moduleCode === 'asset') {
                $moduleIdList = $assetModuleIdList;
            } else {
                $moduleIdList = $shotModuleIdList;
            }
            $where[] = [
                "e.module_id" => ['IN', $moduleIdList],
            ];
        }
        if (!empty($category)) {
            $categoryWhere = [];
            $categoryWhere[] = [
                "json_unquote(json_extract({$entityFilterKey}.json, concat('$.', case {$entityFilterKey}.module_id
                                           when 70 then 'design'
                                           when 44 then 'level'
                                           when 43 then 'asset'
                                           else 'asset' end, '_category')))" => ['IN', $category],
            ];
            if (in_array('动捕', $category)) {
                $categoryWhere[] = ["{$entityFilterKey}.module_id" => module_by_code('sequence')['id']];
            }
            if (count($categoryWhere) > 1) {
                $categoryWhere['_logic'] = 'OR';
            } else {
                $categoryWhere = array_shift($categoryWhere);
            }
            $where[] = $categoryWhere;
        }

        if (in_array($downloadingFilter, ['in', 'notin'])) {
            $downloadingTaskIdList = $this->getDownloadingTaskIdList(request()->getTenantId());
            if ($downloadingFilter === 'in') {
                if (empty($downloadingTaskIdList)) {
                    $where[] = ['task.id' => 0];
                } else {
                    $where[] = ['task.id' => ['IN', implode(',', $downloadingTaskIdList)]];
                }
            } elseif ($downloadingFilter === 'notin' && !empty($downloadingTaskIdList)) {
                $where[] = ['task.id' => ['NOT IN', implode(',', $downloadingTaskIdList)]];
            }
        }

        if (in_array($uploadingFilter, ['in', 'notin'])) {
            $uploadingTaskIdList = $this->getUploadingTaskIdList(request()->getTenantId());
            if ($uploadingFilter === 'in') {
                if (empty($uploadingTaskIdList)) {
                    $where[] = ['task.id' => 0];
                } else {
                    $where[] = ['task.id' => ['IN', implode(',', $uploadingTaskIdList)]];
                }
            } elseif ($uploadingFilter === 'notin' && !empty($uploadingTaskIdList)) {
                $where[] = ['task.id' => ['NOT IN', implode(',', $uploadingTaskIdList)]];
            }
        }

        return $where;
    }

    /**
     * 甲方上传列表
     * @param $param
     * @param int $tenantId
     * @return array
     */
    public function getPartyAUploadList($param, int $tenantId)
    {
        $projectId = $param['project_id'] ?? [];
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $page = get_default_page($param);

        $where = $this->parseListFilter($param['filter'] ?? [], true, false);
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            'task.executor_type' => ['NOT IN', TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP],
        ];
        if (empty($projectId)) {
            $projectList = $this->getProjectList(request()->getTenantId(), ProjectModel::TYPE_INSOURCE);
            $projectId = array_column($projectList, 'id');
        }
        if (empty($projectId)) {
            return $res;
        }
        $baseWhere['`order`.project_id'] = ['IN', $projectId];
        $res['total'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.from_tenant_id = {$tenantId} and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->count('distinct otfl.task_id');
        $res['rows'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.from_tenant_id = {$tenantId} and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->page($page[0], $page[1])
            ->field('otfl.task_id')
            ->distinct(true)
            ->select();
        if (empty($res['rows'])) {
            return $res;
        }
        $taskIdList = array_column($res['rows'], 'task_id');
        $fields = [
            'project.id',
            'project.code',
            'project.work_dir',
            'project.work_dir_prefix',
            'project.engine_dir_prefix',
            'project.is_pack_upload',
            'project.belong_project_id',
            'entity.id',
            'entity.name',
            'entity.code',
            'entity.initial_episode_code',
            'entity.showings_number',
            'entity.module_id',
            'entity.json',
            'task.id',
            'task.name',
            'task.code',
            'task.step_category_id',
            'task.step_id',
            'task.json',
            'max_version.id',
            'max_version.link_id',
        ];
        $res['rows'] = model(TaskModel::class)
            ->join('project on project.id = task.project_id')
            ->join('entity on entity.id = task.entity_id')
            ->join("version max_version on max_version.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->where(['task.id' => ['IN', $taskIdList]])
            ->field(build_complex_query_fields($fields, '__'))
            ->select();
        $maxVersionIdList = [];
        $stepList = StepService::getInstance()->getList([], 'id,name,code,color');
        $stepList = array_column($stepList, null, 'id');
        $stepCategoryList = StepCategoryService::getInstance()->getList([], 'id,name,code,color');
        $stepCategoryList = array_column($stepCategoryList, null, 'id');
        $statusList = StatusService::getInstance()->getList([], 'id,name,color,code');
        $statusList = array_column($statusList, null, 'id');

        foreach ($res['rows'] as &$row) {
            $row = separate_complex_query_data($row, '__');
            $row['module'] = module_by_id($row['entity']['module_id']);
            $moduleCode = $row['module']['code'] ?? 'entity';
            request()->getModuleCode($moduleCode);
            $row['entity'] = CommonService::getFormatAttrData(model(EntityModel::class), $row['entity']);
            request()->getModuleCode('task');
            $row['task'] = CommonService::getFormatAttrData(model(TaskModel::class), $row['task']);
            $row['task_status'] = null;
            if (!empty($row['task']['json']['task_status'])) {
                $row['task_status'] = $statusList[$row['task']['json']['task_status']] ?? null;
            }
            $row['step'] = $stepList[$row['task']['step_id']] ?? null;
            $row['step_category'] = $stepCategoryList[$row['task']['step_category_id']] ?? null;
            $row['max_version_id'] = 0;
            if (!empty($row['max_version']['id'])) {
                $maxVersionIdList[] = $row['max_version']['id'];
                $row['max_version_id'] = $row['max_version']['id'];
            }
        }
        // 供应商列表
        $taskTenantIdRelationList = model(OrderTaskFileListModel::class)
            ->where(['task_id' => ['IN', implode(',', $taskIdList)], 'from_project_id' => ['IN', $projectId]])
            ->field('task_id,to_tenant_id')
            ->distinct(true)
            ->select();
        $tenantList = [];
        if (!empty($taskTenantIdRelationList)) {
            $tenantIdList = array_column($taskTenantIdRelationList, 'to_tenant_id');
            $tenantList = teamones_request('teamones-im', 'tenant/select', [
                'param' => [
                    'filter' => [
                        'tenant.id' => ['-in', implode(',', $tenantIdList)],
                    ],
                ],
            ]);
            $tenantList = array_column($tenantList['data']['rows'], null, 'id');
        }
        $taskTenantIdRelationList = array_group_by($taskTenantIdRelationList, 'task_id');
        $downloadList = [];
        $versionList = [];
        if (!empty($maxVersionIdList)) {
            $versionList = model(VersionModel::class)->where(['id' => ['IN', $maxVersionIdList]])->select();
            request()->getModuleCode('version');
            $versionList = array_map(function ($i) {
                return CommonService::getFormatAttrData(model(VersionModel::class), $i);
            }, $versionList);
            $versionList = array_column($versionList, null, 'id');
            // 外包下载情况查询
            $downloadList = model(OrderTaskUpstreamVersionModel::class)
                ->where([
                    'from_project_id' => ['IN', $projectId],
                    'version_id' => ['IN', implode(',', $maxVersionIdList)],
                ])
                ->field('concat(version_id,"#",to_tenant_id) uk,version_id,to_tenant_id,is_download')
                ->select();
            $downloadList = array_column($downloadList, 'is_download', 'uk');
        }
        unset($row);
        foreach ($res['rows'] as &$row) {
            $row['version'] = null;
            if (!empty($row['max_version_id'])) {
                $row['version'] = $versionList[$row['max_version_id']] ?? null;
            }
            $row['tenant_list'] = [];
            foreach ($taskTenantIdRelationList[$row['task']['id']] ?? [] as $item) {
                if (empty($tenantList[$item['to_tenant_id']])) {
                    continue;
                }
                $tempTenant = $tenantList[$item['to_tenant_id']];
                $downloadStatusKey = $row['max_version_id'] . '#' . $tempTenant['id'];
                $tempTenant['is_download'] = $downloadList[$downloadStatusKey] ?? 'no';
                $row['tenant_list'][] = $tempTenant;
            }
        }
        return $res;
    }

    /**
     * 甲方上传下载统计
     * @param $param
     * @param int $tenantId
     * @return array[]
     */
    public function partyACount($param, int $tenantId)
    {
        $projectIdList = model(ProjectModel::class)
            ->where([
                'belong_project_id' => $param['project_id'],
            ])->field('id')->column('id');
        $res = [
            'upload' => [],
            'download' => [],
        ];
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        if (!empty($projectIdList)) {

            $downloadList = model(OrderModel::class)
                ->join('order_task_list otl on otl.order_id = `order`.id')
                ->join('task on task.id = otl.real_task_id')
                ->join('entity e on e.id = task.entity_id')
                ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'")
                ->join("order_task_upstream_version otuv on otuv.to_project_id = {$param['project_id']} and otuv.version_id = v.id")
                ->where([
                    '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
                    'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
                    '`order`.project_id' => $param['project_id'],
                ])
                ->where([
                    'v.is_uploaded' => 'yes',
                    'otuv.is_download' => 'no',
                ])
                ->group('e.initial_episode_code,e.module_id')
                ->field('e.initial_episode_code episode_code,e.module_id,count(distinct v.id) number')
                ->select();
            $downloadListGrouped = array_group_by($downloadList, 'episode_code');
            foreach ($downloadListGrouped as $episodeCode => $downloadList) {
                $tempLoad = [
                    'episode_code' => $episodeCode,
                    'asset_count' => 0,
                    'shot_count' => 0,
                ];
                foreach ($downloadList as $download) {
                    if (in_array($download['module_id'], $assetModuleIdList)) {
                        $tempLoad['asset_count'] += $download['number'];
                    } else {
                        $tempLoad['shot_count'] += $download['number'];
                    }
                }
                $res['download'][] = $tempLoad;
            }
        }

        $uploadList = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity eu on eu.id = otfl.entity_id')
            ->where([
                // 状态和项目筛选
                '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
                'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
                '`order`.project_id' => $param['project_id'],
                'task.executor_type' => ['NOT IN', TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP],
            ])
            ->where([
                // 待上传筛选
                'v.is_uploaded' => 'no',
                'v.is_publish' => 'yes',
            ])
            ->group('e.initial_episode_code,e.module_id')
            ->field('e.initial_episode_code episode_code,e.module_id,count(distinct v.id) number')
            ->select();
        $uploadListGrouped = array_group_by($uploadList, 'episode_code');
        foreach ($uploadListGrouped as $episodeCode => $uploadList) {
            $tempLoad = [
                'episode_code' => $episodeCode,
                'asset_count' => 0,
                'shot_count' => 0,
            ];
            foreach ($uploadList as $upload) {
                if (in_array($upload['module_id'], $assetModuleIdList)) {
                    $tempLoad['asset_count'] += $upload['number'];
                } else {
                    $tempLoad['shot_count'] += $upload['number'];
                }
            }
            $res['upload'][] = $tempLoad;
        }
        return $res;
    }

    /**
     * 乙方统计
     * @param $param
     * @param int $tenantId
     * @return array[]
     */
    public function partyBCount($param, int $tenantId)
    {
        $project = model(ProjectModel::class)->where(['id' => $param['project_id']])->find();
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $res = [
            'upload' => [],
            'download' => [],
        ];
        if (empty($project) || $project['belong_project_id'] == 0) {
            return $res;
        }
        // 下载  订单甲方已经上传的订单上游任务
        $downloadList = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("order_task_upstream_version otuv on otuv.to_tenant_id = $tenantId and otuv.to_project_id = {$project['id']} and otuv.version_id = task.json->>'$.task_latest_published_version_id'")
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity ed on ed.id = otfl.entity_id')
            ->join('version v on v.id = otuv.version_id')
            ->group('e.initial_episode_code,e.module_id')
            ->field('e.initial_episode_code episode_code,e.module_id,count(distinct v.id) number')
            ->where([
                'v.is_uploaded' => 'yes',
                '`order`.project_id' => $project['belong_project_id'],
                'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
                '`order`.status' => ['NOT IN', [OrderModel::STATUS_INVALID, OrderModel::STATUS_CANCEL]],
                'otuv.is_download' => 'no',
                '`order`.to_tenant_id' => $tenantId,
            ])
            ->select();
        $downloadListGrouped = array_group_by($downloadList, 'episode_code');
        foreach ($downloadListGrouped as $episodeCode => $downloadList) {
            $tempLoad = [
                'episode_code' => $episodeCode,
                'asset_count' => 0,
                'shot_count' => 0,
            ];
            foreach ($downloadList as $download) {
                if (in_array($download['module_id'], $assetModuleIdList)) {
                    $tempLoad['asset_count'] += $download['number'];
                } else {
                    $tempLoad['shot_count'] += $download['number'];
                }
            }
            $res['download'][] = $tempLoad;
        }
        // 上传 订单的任务回收
        $uploadList = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join("order_task_upstream_version otuv on otuv.from_project_id = {$project['id']} and otuv.version_id = task.json->>'$.task_latest_published_version_id'")
            ->join('entity e on e.id = otl.entity_id')
            ->join('version v on v.id = otuv.version_id')
            ->group('e.initial_episode_code,e.module_id')
            ->field('e.initial_episode_code episode_code,e.module_id,count(distinct v.id) number')
            ->where([
                'v.is_uploaded' => 'no',
                'v.is_publish' => 'yes',
                '`order`.project_id' => $project['belong_project_id'],
                'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
                '`order`.status' => ['NOT IN', [OrderModel::STATUS_INVALID, OrderModel::STATUS_CANCEL]],
            ])
            ->select();
        $uploadListGrouped = array_group_by($uploadList, 'episode_code');
        foreach ($uploadListGrouped as $episodeCode => $uploadList) {
            $tempLoad = [
                'episode_code' => $episodeCode,
                'asset_count' => 0,
                'shot_count' => 0,
            ];
            foreach ($uploadList as $upload) {
                if (in_array($upload['module_id'], $assetModuleIdList)) {
                    $tempLoad['asset_count'] += $upload['number'];
                } else {
                    $tempLoad['shot_count'] += $upload['number'];
                }
            }
            $res['upload'][] = $tempLoad;
        }
        return $res;
    }

    /**
     * 甲方项目剧集列表
     * @param $projectId
     * @param int $getTenantId
     * @return array
     */
    public function partyAProjectEpisodeList($projectId, int $getTenantId)
    {
        $episodeCodeList = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('entity e on e.id = otl.entity_id')
            ->field('e.initial_episode_code code')
            ->group('e.initial_episode_code')
            ->column('code');
        if (empty($episodeCodeList)) {
            return [];
        }
        $res = EpisodeService::getInstance()->selectEpisode(['code' => ['IN', $episodeCodeList], 'project_id' => $projectId], 'id,name,code,json');
        request()->getModuleCode('episode');
        foreach ($res as &$resItem) {
            $resItem = CommonService::getFormatAttrData(model(EntityModel::class), $resItem);
        }
        return $res;
    }

    /**
     * 乙方下载列表
     * @param $param
     * @return array
     */
    public function getPartyBDownloadList($param)
    {
        $projectId = $param['project_id'] ?? [];
        if (empty($projectId)) {
            $projectList = $this->getProjectList(request()->getTenantId(), ProjectModel::TYPE_OUTSOURCE);
        } else {
            $projectList = model(ProjectModel::class)
                ->where(['id' => ['IN', $projectId]])
                ->where(['type' => ProjectModel::TYPE_OUTSOURCE])
                ->select();
        }
        $page = get_default_page($param);
        $where = $this->parseListFilter($param['filter'] ?? [], true, false);
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        if (empty($projectList)) {
            return $res;
        }
        $projectTenantId = $projectList[0]['tenant_id'];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => ['IN', array_column($projectList, 'belong_project_id')],
            '`order`.to_tenant_id' => $projectTenantId,
        ];
        $res['total'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.to_tenant_id = {$projectTenantId} and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->count('distinct otfl.task_id');
        $res['rows'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.to_tenant_id = $projectTenantId and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->page($page[0], $page[1])
            ->field('otfl.task_id')
            ->distinct(true)
            ->select();
        if (empty($res['rows'])) {
            return $res;
        }
        $taskIdList = array_column($res['rows'], 'task_id');
        $fields = [
            'project.id',
            'project.code',
            'project.work_dir',
            'project.work_dir_prefix',
            'project.engine_dir_prefix',
            'project.is_pack_upload',
            'project.belong_project_id',
            'entity.id',
            'entity.name',
            'entity.code',
            'entity.initial_episode_code',
            'entity.showings_number',
            'entity.module_id',
            'entity.json',
            'task.id',
            'task.name',
            'task.tenant_id',
            'task.code',
            'task.step_category_id',
            'task.step_id',
            'task.json',
            'max_version.id',
            'max_version.link_id',
            'otuv.id',
            'otuv.task_id',
            'otuv.version_id',
            'otuv.is_download',
            'otuv.updated',
            'otuv.to_project_id',
            'otuv.to_tenant_id',
        ];
        $res['rows'] = model(TaskModel::class)
            ->join('project on project.id = task.project_id')
            ->join('entity on entity.id = task.entity_id')
            ->join("version max_version on max_version.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.to_tenant_id = $projectTenantId and otuv.version_id = max_version.id", 'LEFT')
            ->where(['task.id' => ['IN', $taskIdList]])
            ->field(build_complex_query_fields($fields, '__'))
            ->select();
        $maxVersionIdList = [];
        $stepList = StepService::getInstance()->getList([], 'id,name,code,color');
        $stepList = array_column($stepList, null, 'id');
        $stepCategoryList = StepCategoryService::getInstance()->getList([], 'id,name,code,color');
        $stepCategoryList = array_column($stepCategoryList, null, 'id');
        $statusList = StatusService::getInstance()->getList([], 'id,name,color,code');
        $statusList = array_column($statusList, null, 'id');

        $tenantIdList = [];
        foreach ($res['rows'] as &$row) {
            $row = separate_complex_query_data($row, '__');
            $row['module'] = module_by_id($row['entity']['module_id']);
            $moduleCode = $row['module']['code'] ?? 'entity';
            request()->getModuleCode($moduleCode);
            $row['entity'] = CommonService::getFormatAttrData(model(EntityModel::class), $row['entity']);
            request()->getModuleCode('task');
            $row['task'] = CommonService::getFormatAttrData(model(TaskModel::class), $row['task']);
            $row['task_status'] = null;
            if (!empty($row['task']['json']['task_status'])) {
                $row['task_status'] = $statusList[$row['task']['json']['task_status']] ?? null;
            }
            $row['step'] = $stepList[$row['task']['step_id']] ?? null;
            $row['step_category'] = $stepCategoryList[$row['task']['step_category_id']] ?? null;
            $row['max_version_id'] = 0;
            if (!empty($row['max_version']['id'])) {
                $maxVersionIdList[] = $row['max_version']['id'];
                $row['max_version_id'] = $row['max_version']['id'];
            }
            if (!in_array($row['task']['tenant_id'], $tenantIdList)) {
                $tenantIdList[] = $row['task']['tenant_id'];
            }
        }

        $tenantList = [];
        if (!empty($tenantIdList)) {
            $tenantList = teamones_request('teamones-im', 'tenant/select', [
                'param' => [
                    'filter' => [
                        'tenant.id' => ['-in', implode(',', $tenantIdList)],
                    ],
                ],
            ]);
            $tenantList = array_column($tenantList['data']['rows'], null, 'id');
        }

        $versionList = [];
        if (!empty($maxVersionIdList)) {
            $versionList = model(VersionModel::class)->where(['id' => ['IN', $maxVersionIdList]])->select();
            request()->getModuleCode('version');
            $versionList = array_map(function ($i) {
                return CommonService::getFormatAttrData(model(VersionModel::class), $i);
            }, $versionList);
            $versionList = array_column($versionList, null, 'id');
        }
        unset($row);
        foreach ($res['rows'] as &$row) {
            $row['version'] = null;
            if (!empty($row['max_version_id'])) {
                $row['version'] = $versionList[$row['max_version_id']] ?? null;
            }
            $row['tenant'] = $tenantList[$row['task']['tenant_id']] ?? null;
        }
        return $res;
    }

    /**
     * 乙方上传列表
     * @param $param
     * @return array
     */
    public function getPartyBUploadList($param)
    {
        $projectId = $param['project_id'] ?? [];
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $page = get_default_page($param);
        if (empty($projectId)) {
            $projectList = $this->getProjectList(request()->getTenantId(), ProjectModel::TYPE_OUTSOURCE);
        } else {
            $projectList = model(ProjectModel::class)
                ->where(['id' => ['IN', $projectId]])
                ->where(['type' => ProjectModel::TYPE_OUTSOURCE])
                ->select();
        }
        if (empty($projectList)) {
            return $res;
        }
        $partyAProjectIdList = array_column($projectList, 'belong_project_id');
        $tenantId = $projectList[0]['tenant_id'];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => ['IN', $partyAProjectIdList],
            '`order`.to_tenant_id' => $tenantId,
        ];
        $where = $this->parseListFilter($param['filter'] ?? [], false, false);

        $res['total'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.from_tenant_id = $tenantId and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->count();

        $fields = [
            'e.id',
            'e.name',
            'e.code',
            'e.initial_episode_code',
            'e.showings_number',
            'e.module_id',
            'e.project_id',
            'e.json',
            'task.id',
            'task.name',
            'task.code',
            'task.step_category_id',
            'task.step_id',
            'task.json',
            'v.id',
            'v.number',
            'v.link_id',
            'v.module_id',
            'v.description',
            'v.is_pass',
            'v.is_publish',
            'v.json',
            'v.param',
            'v.created_by',
            'v.created',
            'v.updated',
            'v.is_uploaded',
            'v.pack_name',
            'v.pack_pass',
            'v.cos_upload_name',
            'v.published',
            'otuv.id',
            'otuv.to_tenant_id',
            'otuv.task_id',
            'otuv.version_id',
            'otuv.is_download',
        ];

        $res['rows'] = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.from_tenant_id = $tenantId and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where)
            ->field(build_complex_query_fields($fields, '__'))
            ->page($page[0], $page[1])
            ->select();

        $stepList = StepService::getInstance()->getList([], 'id,name,code,color');
        $stepList = array_column($stepList, null, 'id');
        $stepCategoryList = StepCategoryService::getInstance()->getList([], 'id,name,code,color');
        $stepCategoryList = array_column($stepCategoryList, null, 'id');
        $statusList = StatusService::getInstance()->getList([], 'id,name,color,code');
        $statusList = array_column($statusList, null, 'id');
        $tenantIdList = [];
        $projectIdList = [];
        foreach ($res['rows'] as &$row) {
            $row = separate_complex_query_data($row, '__');
            $row['module'] = module_by_id($row['e']['module_id']);
            $moduleCode = $row['module']['code'] ?? 'entity';
            request()->getModuleCode($moduleCode);
            $row['entity'] = CommonService::getFormatAttrData(model(EntityModel::class), $row['e']);
            request()->getModuleCode('task');
            $row['task'] = CommonService::getFormatAttrData(model(TaskModel::class), $row['task']);
            $row['task_status'] = null;
            if (!empty($row['task']['json']['task_status'])) {
                $row['task_status'] = $statusList[$row['task']['json']['task_status']] ?? null;
            }
            $row['step'] = $stepList[$row['task']['step_id']] ?? null;
            $row['step_category'] = $stepCategoryList[$row['task']['step_category_id']] ?? null;
            $row['max_version'] = $row['version'] = CommonService::getFormatAttrData(model(VersionModel::class), $row['v']);
            $row['max_version_id'] = $row['max_version']['id'];
            if (!in_array($row['otuv']['to_tenant_id'], $tenantIdList)) {
                $tenantIdList[] = $row['otuv']['to_tenant_id'];
            }
            $projectIdList[$row['entity']['project_id']] = $row['entity']['project_id'];
            unset($row['v'], $row['e']);
        }
        $tenantList = [];
        if (!empty($tenantIdList)) {
            $tenantList = teamones_request('teamones-im', 'tenant/select', [
                'param' => [
                    'filter' => [
                        'tenant.id' => ['-in', implode(',', $tenantIdList)],
                    ],
                ],
            ]);
            $tenantList = array_column($tenantList['data']['rows'], null, 'id');
        }
        $projectList = [];
        if (!empty($projectIdList)) {
            $projectList = model(ProjectModel::class)
                ->where(['id' => ['IN', implode(',', $projectIdList)]])
                ->field([
                    'id',
                    'code',
                    'work_dir',
                    'work_dir_prefix',
                    'engine_dir_prefix',
                    'belong_project_id',
                    'is_pack_upload',
                ])
                ->select();
            $projectList = array_column($projectList, null, 'id');
        }
        foreach ($res['rows'] as &$row) {
            $row['tenant'] = null;
            if (!empty($row['otuv']['to_tenant_id'])) {
                $row['tenant'] = $tenantList[$row['otuv']['to_tenant_id']] ?? null;
            }
            $row['project'] = null;
            if (!empty($projectList[$row['entity']['project_id']])) {
                $row['project'] = $projectList[$row['entity']['project_id']];
            }
        }
        return $res;
    }

    /**
     * 甲方上传统计
     *
     * @param array $param
     * @param int $tenantId
     * @return array
     */
    public function getPartyAUploadCount($param, int $tenantId)
    {
        $projectId = $param['project_id'];
        $where = $this->parseListFilter($param['filter'] ?? [], true, false);

        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $shotModuleIdList = [
//            module_by_code('shot')['id']
            module_by_code('sequence')['id'],
        ];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => $projectId,
            'task.executor_type' => ['NOT IN', TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP],
        ];
        $countQuery = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.from_tenant_id = {$tenantId} and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where);
        $res = [];

        $countRes = $countQuery
            ->join('module on module.id = entity.module_id')
            ->group("entity.module_id,json_unquote(json_extract(entity.json,concat('$.',module.code,'_category')))")
            ->field("entity.module_id,json_unquote(json_extract(entity.json,concat('$.',module.code,'_category'))) category,count(distinct task.entity_id) number")
            ->select();

        foreach ($countRes as $countResItem) {
            if (in_array($countResItem['module_id'], $assetModuleIdList)) {
                $res[] = [
                    'category' => $countResItem['category'],
                    'number' => $countResItem['number'],
                ];
            } elseif (in_array($countResItem['module_id'], $shotModuleIdList)) {
                $res[] = [
                    'category' => '动捕',
                    'number' => $countResItem['number'],
                ];
            } else {

            }
        }
        return $res;
    }

    /**
     * 甲方下载统计
     * @param $param
     * @return array
     */
    public function getPartyADownloadCount($param)
    {
        $projectId = $param['project_id'];
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $shotModuleIdList = [
//            module_by_code('shot')['id']
            module_by_code('sequence')['id'],
        ];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => $projectId,
        ];
        $where = $this->parseListFilter($param['filter'] ?? [], true, true);
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $partyBProjectIdList = model(ProjectModel::class)
            ->where(['belong_project_id' => $projectId])
            ->field('id')
            ->column('id');
        if (empty($partyBProjectIdList)) {
            return $res;
        }
        $countQuery = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.to_project_id = $projectId and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where);
        $res = [];
        $countRes = $countQuery
            ->join('module on module.id = e.module_id')
            ->group("e.module_id,json_unquote(json_extract(e.json, concat('$.', module.code, '_category')))")
            ->field("e.module_id,json_unquote(json_extract(e.json, concat('$.', module.code, '_category'))) category, count( e.id) number")
            ->select();

        foreach ($countRes as $countResItem) {
            if (in_array($countResItem['module_id'], $assetModuleIdList)) {
                $res[] = [
                    'category' => $countResItem['category'],
                    'number' => $countResItem['number'],
                ];
            } elseif (in_array($countResItem['module_id'], $shotModuleIdList)) {
                $res[] = [
                    'category' => '动捕',
                    'number' => $countResItem['number'],
                ];
            } else {

            }
        }
        return $res;
    }

    /**
     * 乙方上传统计
     * @param $param
     * @return array|array[]
     */
    public function getPartyBUploadCount($param)
    {
        $projectId = $param['project_id'];

        $project = model(ProjectModel::class)->find($projectId);
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $shotModuleIdList = [
//            module_by_code('shot')['id']
            module_by_code('sequence')['id'],
        ];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => $project['belong_project_id'],
            '`order`.to_tenant_id' => $project['tenant_id'],
        ];
        $where = $this->parseListFilter($param['filter'] ?? [], false, false);
        $countQuery = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('task on task.id = otl.real_task_id')
            ->join('entity e on e.id = task.entity_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join("order_task_upstream_version otuv on otuv.from_project_id = $projectId and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where);

        $res = [];

        $countRes = $countQuery
            ->join('module on module.id = e.module_id')
            ->group("e.module_id,json_unquote(json_extract(e.json, concat('$.', module.code, '_category')))")
            ->field("e.module_id,json_unquote(json_extract(e.json, concat('$.', module.code, '_category'))) category, count(e.id) number")
            ->select();
        foreach ($countRes as $countResItem) {
            if (in_array($countResItem['module_id'], $assetModuleIdList)) {
                $res[] = [
                    'category' => $countResItem['category'],
                    'number' => $countResItem['number'],
                ];
            } elseif (in_array($countResItem['module_id'], $shotModuleIdList)) {
                $res[] = [
                    'category' => '动捕',
                    'number' => $countResItem['number'],
                ];
            } else {

            }
        }
        return $res;
    }

    /**
     * 乙方下载统计
     * @param $param
     * @param int $getTenantId
     * @return array|array[]
     */
    public function getPartyBDownloadCount($param, int $getTenantId)
    {
        $projectId = $param['project_id'];
        $project = model(ProjectModel::class)->find($projectId);
        $where = $this->parseListFilter($param['filter'] ?? [], true, false);
        $assetModuleIdList = [
            module_by_code('design')['id'],
            module_by_code('asset')['id'],
            module_by_code('level')['id'],
        ];
        $shotModuleIdList = [
//            module_by_code('shot')['id']
            module_by_code('sequence')['id'],
        ];
        $baseWhere = [
            '`order`.status' => ['NOT IN', [OrderModel::STATUS_CANCEL, OrderModel::STATUS_INVALID]],
            'otl.status' => OrderTaskListModel::STATUS_DEFAULT,
            '`order`.project_id' => $project['belong_project_id'],
            '`order`.to_tenant_id' => $project['tenant_id'],
        ];
        $countQuery = model(OrderModel::class)
            ->join('order_task_list otl on otl.order_id = `order`.id')
            ->join('order_task_file_list otfl on otfl.order_id = `order`.id and otfl.downstream_task_id = otl.real_task_id')
            ->join('task on task.id = otfl.task_id')
            ->join("version v on v.id = task.json->>'$.task_latest_published_version_id'", 'LEFT')
            ->join('entity e on e.id = otl.entity_id')
            ->join('entity on entity.id = otfl.entity_id')
            ->join("order_task_upstream_version otuv on otuv.to_project_id = {$projectId} and otuv.version_id = v.id", 'LEFT')
            ->where($baseWhere)
            ->where($where);

        $res = [];

        $countRes = $countQuery
            ->join('module on module.id = entity.module_id')
            ->group("entity.module_id,json_unquote(json_extract(entity.json, concat('$.', module.code, '_category')))")
            ->field("entity.module_id,json_unquote(json_extract(entity.json, concat('$.', module.code, '_category'))) category, count(distinct task.entity_id) number")
            ->select();
        foreach ($countRes as $countResItem) {
            if (in_array($countResItem['module_id'], $assetModuleIdList)) {
                $res[] = [
                    'category' => $countResItem['category'],
                    'number' => $countResItem['number'],
                ];
            } elseif (in_array($countResItem['module_id'], $shotModuleIdList)) {
                $res[] = [
                    'category' => '动捕',
                    'number' => $countResItem['number'],
                ];
            } else {

            }
        }
        return $res;
    }

    /**
     * 标记为正在下载中
     * @param int $taskId
     * @param $downloadExtraInfo
     * @param int $tenantId
     * @return true
     */
    public function markDownloading(int $taskId, $downloadExtraInfo, int $tenantId)
    {
        return TenantDownloadUploadQueueService::getInstance()->markInprogress($taskId, $downloadExtraInfo, $tenantId, TenantDownloadUploadQueueService::MARK_DOWNLOAD);
    }

    /**
     * 清空下载中的标记
     * @param $tenantId
     * @param $taskId
     * @return void
     */
    private function clearDownloading($tenantId, $taskId)
    {
        TenantDownloadUploadQueueService::getInstance()->removeTaskFromInprogress($taskId, $tenantId, TenantDownloadUploadQueueService::MARK_DOWNLOAD);
    }

    /**
     * 移除上传中标识
     * @param array $versionIdList
     * @param int $tenantId
     * @return int
     */
    public function removeUploadingMark(array $versionIdList, int $tenantId)
    {
        $keys = array_map(function ($item) use ($tenantId) {
            return "version_uploading_$tenantId:$item";
        }, $versionIdList);
        return Redis::del(...$keys);
    }

    /**
     * 订单甲方上传列表
     * @param int $orderId
     * @param $tenantId
     * @return array
     */
    public function getOrderPartyAUploadList(int $orderId, $tenantId, $page)
    {
        $order = model(OrderModel::class)->find($orderId);
        $orderTaskList = $this->orderTaskListModel
            ->alias('otl')
            ->join('order_task_file_list otfl on otl.order_id = otfl.order_id and otl.real_task_id = otfl.downstream_task_id and otfl.from_tenant_id = ' . $tenantId)
            ->where(['otl.order_id' => $orderId, 'otl.status' => OrderTaskListModel::STATUS_DEFAULT])
            ->field('distinct otl.real_task_id')
            ->select();
        if (empty($orderTaskList)) {
            return [];
        }
        $taskList = model(TaskModel::class)->where(['id' => ['IN', array_column($orderTaskList, 'real_task_id')]])
            ->page(...$page)
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $project = model(ProjectModel::class)->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,is_pack_upload,belong_project_id')->find($order['project_id']);
        $taskList = array_column($taskList, null, 'id');
        $statusIdList = array_column($taskList, 'task_status_id', 'task_status_id');
        $entityIdList = array_column($taskList, 'entity_id');
        $stepCategoryIdList = array_column($taskList, 'step_category_id');
        $orderTaskFileListList = $this->orderTaskFileListModel->where(['order_id' => $orderId, 'from_tenant_id' => $tenantId])->select();
        // 没有上游依赖任务
        if (empty($orderTaskFileListList)) {
            $entityList = model(EntityModel::class)->where(['id' => ['IN', $entityIdList]])->field('id,name,code,entity_id,entity_module_id,showings_number,initial_episode_code')->select();
            $entityList = array_column($entityList, null, 'id');
            $statusList = model(StatusModel::class)->where(['id' => ['IN', $statusIdList]])->select();
            $statusList = array_column($statusList, null, 'id');
            foreach ($taskList as &$taskListItem) {
                $taskListItem['entity'] = $entityList[$taskListItem['entity_id']] ?? null;
                $taskListItem['task_status'] = $statusList[$taskListItem['task_status_id']] ?? null;
            }
            return array_values($taskList);
        }
        $upstreamTaskIdList = array_column($orderTaskFileListList, 'task_id', 'task_id');

        $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)->where(['to_tenant_id' => $order['to_tenant_id'], 'task_id' => ['IN', $upstreamTaskIdList]])->select();
        $orderTaskUpstreamVersionList = array_group_by($orderTaskUpstreamVersionList, 'task_id');

        $upstreamTaskList = model(TaskModel::class)->where(['id' => ['IN', $upstreamTaskIdList]])
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        $taskModuleId = Module::$moduleDictData['module_index_by_code']['task']['id'];
        $upstreamVersionList = model(VersionModel::class)->where(['module_id' => $taskModuleId, 'link_id' => ['IN', $upstreamTaskIdList]])->select();
        $upstreamVersionList = array_column($upstreamVersionList, null, 'id');
        $upstreamEntityIdList = array_column($upstreamTaskList, 'entity_id');
        $upstreamTaskStatusIdList = array_column($upstreamTaskList, 'task_status_id', 'task_status_id');
        $upstreamTaskList = array_column($upstreamTaskList, null, 'id');

        //工序--环节
        $upstreamStepCategoryIdList = array_column($upstreamTaskList, 'step_category_id');
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', array_merge($upstreamStepCategoryIdList, $stepCategoryIdList)]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $entityList = model(EntityModel::class)->where(['id' => ['IN', array_merge($upstreamEntityIdList, $entityIdList)]])->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,entity_id,entity_module_id,showings_number,initial_episode_code")->select();
        $entityList = array_column($entityList, null, 'id');

        $statusList = model(StatusModel::class)->where(['id' => ['IN', array_merge($upstreamTaskStatusIdList, $statusIdList)]])->select();
        $statusList = array_column($statusList, null, 'id');

        $orderTaskFileListList = array_group_by($orderTaskFileListList, 'downstream_task_id');
        $res = [];
        foreach ($orderTaskFileListList as $downstreamTaskId => $upstreamTaskFileList) {
            if (empty($taskList[$downstreamTaskId])) {
                continue;
            }
            $tmpTask = $taskList[$downstreamTaskId];

            $tmpTask['module']['code'] = module_by_id($tmpTask['entity_module_id'])['code'];
            $tmpTask['step_category'] = $stepCategoryList[$tmpTask['step_category_id']] ?? null;

            $tmpTask['entity'] = $entityList[$tmpTask['entity_id']] ?? null;
            $tmpTask['task_status'] = $statusList[$tmpTask['task_status_id']] ?? null;
            $upstreamTaskFileList = array_group_by($upstreamTaskFileList, 'task_id');
            foreach ($upstreamTaskFileList as $taskId => $taskVersions) {
                $tmpTaskChildren = $upstreamTaskList[$taskId] ?? null;
                if (empty($tmpTaskChildren)) {
                    continue;
                }
                $taskVersions = $orderTaskUpstreamVersionList[$taskId] ?? [];
                $taskVersions = array_sort_by($taskVersions, 'version_id', 'desc', false);
                $tmpTaskChildren['entity'] = $entityList[$tmpTaskChildren['entity_id']] ?? null;
                $tmpTaskChildren['task_status'] = $statusList[$tmpTaskChildren['task_status_id']] ?? null;

                $tmpTaskChildren['module']['code'] = module_by_id($tmpTaskChildren['entity_module_id'])['code'];
                $tmpTaskChildren['step_category'] = $stepCategoryList[$tmpTaskChildren['step_category_id']] ?? null;

                $tmpTaskChildren['uploaded'] = false;
                $tmpTaskChildren['has_update'] = false;
                $tmpTaskChildren['project'] = $project;
                // 当前已上传版本 和当前最新版本
                $tmpTaskChildren['current_version'] = $tmpTaskChildren['latest_version'] = null;

                if (empty($taskVersions)) {
                    $tmpTaskChildren['dont_have_version'] = true;
                } else {
                    // 检查有没有上传过
                    foreach ($taskVersions as $version) {
                        $version = $upstreamVersionList[$version['version_id']] ?? null;
                        if (empty($version)) {
                            continue;
                        }
                        if ($version['is_uploaded'] === 'yes') {
                            if (empty($tmpTaskChildren['current_version'])) {
                                $tmpTaskChildren['current_version'] = $version;
                            }
                            $tmpTaskChildren['uploaded'] = true;
                        }
                    }
                    $maxVersion = $taskVersions[0];
                    $tmpTaskChildren['latest_version'] = $tmpTaskChildren['version'] = $upstreamVersionList[$maxVersion['version_id']] ?? null;
                    if (!empty($tmpTaskChildren['version']) && $tmpTaskChildren['version']['is_uploaded'] === 'no') {
                        $tmpTaskChildren['has_update'] = true;
                    }
                }

                $tmpTask['children'][] = $tmpTaskChildren;
            }
            $res[] = $tmpTask;
        }
        return $res;
    }

    /**
     * 甲方下载列表
     * @param int $orderId
     * @param $tenantId
     * @return array
     */
    public function getOrderPartyADownloadList(int $orderId, $tenantId, $page)
    {
        $order = model(OrderModel::class)->find($orderId);
        $orderTaskList = $this->orderTaskListModel->where(['order_id' => $orderId, 'status' => OrderTaskListModel::STATUS_DEFAULT])->select();
        if (empty($orderTaskList)) {
            return [];
        }
        $taskIdList = array_column($orderTaskList, 'real_task_id');
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])
            ->page(...$page)
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        if (empty($taskList)) {
            return [];
        }

        $project = model(ProjectModel::class)->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,is_pack_upload,belong_project_id')->find($order['project_id']);
        $orderTaskFileListList = model(OrderTaskFileListModel::class)->where(['order_id' => $orderId, 'to_tenant_id' => $tenantId])->select();
        $orderTaskFileListList = array_group_by($orderTaskFileListList, 'task_id');

        $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)
            ->alias('otuv')
            ->join('version v on v.id = otuv.version_id')
            ->where(['otuv.from_tenant_id' => $order['to_tenant_id'], 'otuv.to_tenant_id' => $order['from_tenant_id'], 'otuv.task_id' => ['IN', $taskIdList], 'v.is_uploaded' => 'yes'])
            ->field('otuv.*')
            ->select();
        $orderTaskUpstreamVersionList = array_group_by($orderTaskUpstreamVersionList, 'task_id');
        $taskModuleId = Module::$moduleDictData['module_index_by_code']['task']['id'];

        $taskList = array_column($taskList, null, 'id');
        $statusIdList = array_column($taskList, 'task_status_id', 'task_status_id');
        $entityIdList = array_column($taskList, 'entity_id');

        //工序--环节
        $stepCategoryIdList = array_column($taskList, 'step_category_id');
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $entityList = model(EntityModel::class)->where(['id' => ['IN', $entityIdList]])->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,entity_id,entity_module_id,showings_number,initial_episode_code")->select();
        $entityList = array_column($entityList, null, 'id');

        $statusList = model(StatusModel::class)->where(['id' => ['IN', $statusIdList]])->select();
        $statusList = array_column($statusList, null, 'id');

        $taskVersionList = model(VersionModel::class)->where(['module_id' => $taskModuleId, 'link_id' => ['IN', $taskIdList]])->select();
        $taskVersionList = array_column($taskVersionList, null, 'id');
        foreach ($taskList as &$task) {
            $task['module']['code'] = module_by_id($task['entity_module_id'])['code'];

            $task['step_category'] = $stepCategoryList[$task['step_category_id']] ?? null;

            $task['project'] = $project;
            $task['entity'] = $entityList[$task['entity_id']] ?? null;
            $task['task_status'] = $statusList[$task['task_status_id']] ?? null;
            $currentOrderTaskFileList = $orderTaskFileListList[$task['id']] ?? [];
            $currentOrderTaskUpstreamVersionList = $orderTaskUpstreamVersionList[$task['id']] ?? [];
            $task['uploaded'] = false;
            $task['has_update'] = false;
            $task['downloaded'] = false;
            // 已下载版本和最新版本
            $task['current_version'] = $task['latest_version'] = null;

            if (empty($currentOrderTaskFileList)) {
                continue;
            }
            $currentOrderTaskUpstreamVersionList = array_sort_by($currentOrderTaskUpstreamVersionList, 'version_id', 'desc', false);
            $downloadedVersion = [];
            foreach ($currentOrderTaskUpstreamVersionList as $item) {
                if ($item['is_download'] === 'yes') {
                    $task['downloaded'] = true;
                    $downloadedVersion[] = $item['version_id'];
                    if (empty($task['current_version'])) {
                        $task['current_version'] = $taskVersionList[$item['version_id']] ?? null;
                    }
                }
                $version = $taskVersionList[$item['version_id']] ?? null;
                if (!empty($version) && $version['is_uploaded'] === 'yes') {
                    $task['uploaded'] = true;
                    if (empty($task['version'])) {
                        $task['version'] = $version;
                        $task['order_task_upstream_version'] = $item;
                    }
                    if (empty($task['latest_version'])) {
                        $task['latest_version'] = $version;
                    }
                }
            }
            if (!empty($task['order_task_upstream_version'])) {
                $maxVersion = $taskVersionList[$task['order_task_upstream_version']['version_id']] ?? null;
                // 最后一个版本上传了 但是没有下载 那么就设置有更新标记
                if (!empty($maxVersion) && $maxVersion['is_uploaded'] && !in_array($maxVersion['id'], $downloadedVersion) && $task['downloaded']) {
                    // 已经下载过就设置为有更新
                    $task['has_update'] = true;
                }
            }
        }

        return array_values($taskList);
    }

    /**
     * 订单乙方上传列表
     * @param int $orderId
     * @param $tenantId
     * @param $page
     * @return array
     */
    public function getOrderPartyBUploadList(int $orderId, $tenantId, $page)
    {
        $orderTaskList = $this->orderTaskListModel->where(['order_id' => $orderId, 'status' => OrderTaskListModel::STATUS_DEFAULT])->select();
        if (empty($orderTaskList)) {
            return [];
        }
        $taskIdList = array_column($orderTaskList, 'real_task_id');
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])
            ->page(...$page)
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,project_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $firstTask = $taskList[0];
        $project = model(ProjectModel::class)->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,is_pack_upload,belong_project_id')->find($firstTask['project_id']);

        $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)->where(['task_id' => ['IN', $taskIdList]])->select();
        $orderTaskUpstreamVersionList = array_group_by($orderTaskUpstreamVersionList, 'task_id');
        $taskModuleId = Module::$moduleDictData['module_index_by_code']['task']['id'];

        $taskList = array_column($taskList, null, 'id');
        $statusIdList = array_column($taskList, 'task_status_id', 'task_status_id');
        $entityIdList = array_column($taskList, 'entity_id');
        //工序--环节
        $stepCategoryIdList = array_column($taskList, 'step_category_id');
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $entityList = model(EntityModel::class)->where(['id' => ['IN', $entityIdList]])->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,entity_id,entity_module_id,showings_number,initial_episode_code")->select();

        $entityList = array_column($entityList, null, 'id');

        $statusList = model(StatusModel::class)->where(['id' => ['IN', $statusIdList]])->select();
        $statusList = array_column($statusList, null, 'id');

        $taskVersionList = model(VersionModel::class)->where(['module_id' => $taskModuleId, 'link_id' => ['IN', $taskIdList]])->select();
        $taskVersionList = array_column($taskVersionList, null, 'id');
        foreach ($taskList as &$task) {
            $task['module']['code'] = module_by_id($task['entity_module_id'])['code'];
            $task['step_category'] = $stepCategoryList[$task['step_category_id']] ?? null;

            $task['project'] = $project;
            $task['entity'] = $entityList[$task['entity_id']] ?? null;
            $task['task_status'] = $statusList[$task['task_status_id']] ?? null;
            $currentOrderTaskUpstreamVersionList = $orderTaskUpstreamVersionList[$task['id']] ?? [];
            $task['uploaded'] = false;
            $task['has_update'] = false;
            $task['downloaded'] = false;
            $task['current_version'] = $task['latest_version'] = null;
            if (empty($currentOrderTaskUpstreamVersionList)) {
                continue;
            }
            $currentOrderTaskUpstreamVersionList = array_sort_by($currentOrderTaskUpstreamVersionList, 'version_id', 'desc', false);

            foreach ($currentOrderTaskUpstreamVersionList as $item) {
                $version = $taskVersionList[$item['version_id']] ?? null;
                if (!empty($version) && $version['is_uploaded'] === 'yes') {
                    $task['uploaded'] = true;
                    if (empty($task['version'])) {
                        $task['version'] = $version;
                    }
                    if (empty($task['current_version'])) {
                        $task['current_version'] = $version;
                    }
                }
            }
            $maxVersion = $currentOrderTaskUpstreamVersionList[0];
            if ($maxVersion['version_id'] > 0) {
                $maxVersion = $taskVersionList[$maxVersion['version_id']] ?? null;
                if (!empty($maxVersion)) {
                    $task['latest_version'] = $task['version'] = $maxVersion;
                }
                // 最后一个版本没有上传 标记存在更新 需要上传
                if (!empty($maxVersion) && $maxVersion['is_uploaded'] === 'no') {
                    $task['has_update'] = true;
                    $task['version'] = $maxVersion;
                }
            }
        }

        return array_values($taskList);
    }

    /**
     * 乙方下载列表
     * @param int $orderId
     * @param int $tenantId
     * @param $page
     * @return array
     */
    public function getOrderPartyBDownloadList(int $orderId, int $tenantId, $page)
    {
        $orderTaskList = $this->orderTaskListModel
            ->alias('otl')
            ->join('order_task_file_list otfl on otl.order_id = otfl.order_id and otl.real_task_id = otfl.downstream_task_id and otfl.to_tenant_id = ' . $tenantId)
            ->where(['otl.order_id' => $orderId, 'otl.status' => OrderTaskListModel::STATUS_DEFAULT])
            ->field('distinct otl.real_task_id')
            ->select();
        if (empty($orderTaskList)) {
            return [];
        }
        $taskList = model(TaskModel::class)->where(['id' => ['IN', array_column($orderTaskList, 'real_task_id')]])
            ->page(...$page)
            ->field("id,name,code,project_id,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $project = model(ProjectModel::class)->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,is_pack_upload,belong_project_id')->find($taskList[0]['project_id']);
        $taskList = array_column($taskList, null, 'id');
        $statusIdList = array_column($taskList, 'task_status_id', 'task_status_id');
        $entityIdList = array_column($taskList, 'entity_id');
        $stepCategoryIdList = array_column($taskList, 'step_category_id');
        $orderTaskFileListList = $this->orderTaskFileListModel->where(['order_id' => $orderId, 'to_tenant_id' => $tenantId])->select();
        // 没有上游依赖
        if (empty($orderTaskFileListList)) {
            return [];
        }
        $upstreamTaskIdList = array_column($orderTaskFileListList, 'task_id', 'task_id');

        $upstreamTaskList = model(TaskModel::class)->where(['id' => ['IN', $upstreamTaskIdList]])
            ->field("id,name,code,entity_id,step_category_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id,executor_type,executor_tenant_id")
            ->select();
        $taskModuleId = Module::$moduleDictData['module_index_by_code']['task']['id'];
        $upstreamVersionList = model(VersionModel::class)->where(['module_id' => $taskModuleId, 'link_id' => ['IN', $upstreamTaskIdList], 'is_publish' => 'yes'])->select();
        $upstreamVersionList = array_column($upstreamVersionList, null, 'id');

        // 已通过版本 倒序排列map
        $upstreamVersionListGroupByTaskId = array_group_by($upstreamVersionList, 'link_id');
        foreach ($upstreamVersionListGroupByTaskId as &$tmpVersionList) {
            $tmpVersionList = array_filter($tmpVersionList, function ($item) {
                return $item['is_publish'] === 'yes';
            });
            $tmpVersionList = array_sort_by($tmpVersionList, 'number', 'desc');
        }

        $upstreamEntityIdList = array_column($upstreamTaskList, 'entity_id');
        $upstreamTaskStatusIdList = array_column($upstreamTaskList, 'task_status_id', 'task_status_id');
        $upstreamTaskList = array_column($upstreamTaskList, null, 'id');

        $upstreamStepCategoryIdList = array_column($upstreamTaskList, 'step_category_id');
        // 查询出甲方已经上传的版本信息
        $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)
            ->alias('otuv')
            ->join('version v on v.id = otuv.version_id')
            ->where(['otuv.to_tenant_id' => $tenantId, 'otuv.task_id' => ['IN', $upstreamTaskIdList], 'v.is_uploaded' => 'yes'])
            ->field('otuv.*')
            ->select();
        $orderTaskUpstreamVersionList = array_group_by($orderTaskUpstreamVersionList, 'task_id');

        //工序--环节
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', array_merge($upstreamStepCategoryIdList, $stepCategoryIdList)]])->field('id,code')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'id');

        $entityList = model(EntityModel::class)->where(['id' => ['IN', array_merge($upstreamEntityIdList, $entityIdList)]])->field("id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,'$.asset_category')) as asset_category,entity_id,entity_module_id,showings_number,initial_episode_code")->select();
        $entityList = array_column($entityList, null, 'id');

        $statusList = model(StatusModel::class)->where(['id' => ['IN', array_merge($upstreamTaskStatusIdList, $statusIdList)]])->select();
        $statusList = array_column($statusList, null, 'id');

        $orderTaskFileListList = array_group_by($orderTaskFileListList, 'downstream_task_id');
        $res = [];
        foreach ($orderTaskFileListList as $downstreamTaskId => $upstreamTaskFileList) {
            if (empty($taskList[$downstreamTaskId])) {
                continue;
            }
            $tmpTask = $taskList[$downstreamTaskId];

            $tmpTask['module']['code'] = module_by_id($tmpTask['entity_module_id'])['code'];
            $tmpTask['step_category'] = $stepCategoryList[$tmpTask['step_category_id']] ?? null;

            $tmpTask['entity'] = $entityList[$tmpTask['entity_id']] ?? null;
            $tmpTask['task_status'] = $statusList[$tmpTask['task_status_id']] ?? null;
            $upstreamTaskFileList = array_group_by($upstreamTaskFileList, 'task_id');
            foreach ($upstreamTaskFileList as $taskId => $taskVersions) {
                $taskVersions = $orderTaskUpstreamVersionList[$taskId] ?? [];
                $tmpTaskChildren = $upstreamTaskList[$taskId] ?? null;
                if (empty($tmpTaskChildren)) {
                    continue;
                }
                $tmpTaskChildren['project'] = $project;
                $taskVersions = array_sort_by($taskVersions, 'version_id', 'desc', false);

                $tmpTaskChildren['module']['code'] = module_by_id($tmpTaskChildren['entity_module_id'])['code'];
                $tmpTaskChildren['step_category'] = $stepCategoryList[$tmpTaskChildren['step_category_id']] ?? null;

                $tmpTaskChildren['entity'] = $entityList[$tmpTaskChildren['entity_id']] ?? null;
                $tmpTaskChildren['task_status'] = $statusList[$tmpTaskChildren['task_status_id']] ?? null;
                $tmpTaskChildren['uploaded'] = false;
                $tmpTaskChildren['has_update'] = false;
                $tmpTaskChildren['downloaded'] = false;
                $tmpTaskChildren['latest_version'] = $tmpTaskChildren['current_version'] = null;
                $downloadedVersion = [];
                // 检查有没有上传过
                foreach ($taskVersions as $versionData) {
                    $version = $upstreamVersionList[$versionData['version_id']] ?? null;
                    if (empty($version)) {
                        continue;
                    }
                    if ($versionData['is_download'] === 'yes') {
                        $tmpTaskChildren['downloaded'] = true;
                        $downloadedVersion[] = $versionData['version_id'];
                        if (empty($tmpTaskChildren['current_version'])) {
                            // 已下载版本
                            $tmpTaskChildren['current_version'] = $version;
                        }
                    }
                    if ($version['is_uploaded'] === 'yes') {
                        $tmpTaskChildren['uploaded'] = true;
                        // 显示最新上传的版本数据
                        if (empty($tmpTaskChildren['order_task_upstream_version'])) {
                            $tmpTaskChildren['order_task_upstream_version'] = $versionData;
                            $tmpTaskChildren['version'] = $version;
                        }
                    }
                }
                // 最新版本
                if (!empty($upstreamVersionListGroupByTaskId[$taskId])) {
                    $tmpTaskChildren['latest_version'] = array_shift($upstreamVersionListGroupByTaskId[$taskId]);
                } else {
                    $tmpTaskChildren['latest_version'] = null;
                }
                if (empty($taskVersions)) {
                    $tmpTaskChildren['dont_have_version'] = true;
                } else {
                    // 最新版本没有下载过 并且下载过其他版本 没有下载过当前版本 那么就标记为有更新
                    if (!empty($tmpTaskChildren['version']) && $tmpTaskChildren['version']['is_uploaded'] === 'yes' && !empty($downloadedVersion) && !in_array($tmpTaskChildren['version']['id'], $downloadedVersion)) {
                        $tmpTaskChildren['has_update'] = true;
                    }
                }
                $tmpTask['children'][] = $tmpTaskChildren;
            }
            $res[] = $tmpTask;
        }
        return $res;
    }

    /**
     * 甲方标记已下载
     * @param $id
     * @return array
     * @throws Throwable
     */
    public function partyAMarkDownloaded($id)
    {
        $orderTaskFileList = $this->orderTaskFileListModel->find($id);
        if (empty($orderTaskFileList)) {
            throw new LogicException("order task file list not found", ErrorCode::DATA_INVALID);
        }
        $versionService = VersionService::getInstance();
        $version = $versionService->getVersionById($orderTaskFileList['version_id']);
        $projectList = model(ProjectModel::class)->where(['id' => ['IN', [$orderTaskFileList['from_project_id'], $orderTaskFileList['to_project_id']]]])->select();
        $projectList = array_column($projectList, null, 'id');
        // 检查是不是乙方传给甲方
        if ($projectList[$orderTaskFileList['from_project_id']] !== 'outsource' || $projectList[$orderTaskFileList['to_project_id']] !== 'insource') {
            throw new LogicException("DATA INVALID", ErrorCode::DATA_INVALID);
        }
        $toVersion = $version;
        $toVersion['tenant_id'] = $orderTaskFileList['to_tenant_id'];
        $toVersion['project_id'] = $orderTaskFileList['to_project_id'];
        $toVersion['link_id'] = $orderTaskFileList['downstream_task_id'];
        $statusService = StatusService::getInstance();
        $taskDoneStatus = $statusService->getTaskDoneStatus();
        $res = [];

        model(TaskModel::class)->startTrans();
        try {
            $res['order_task_file_list'] = $this->orderTaskFileListModel->modifyItem(['id' => $id, 'is_download' => 'yes']);
            $res['version'] = model(VersionModel::class)->addItem($toVersion);
            // 更新任务状态为已完成
            $res['update_version'] = model(TaskModel::class)->execute("update task set json = json_set(IFNULL(json,'{}'),'$.task_status',{$taskDoneStatus['id']}) where id = {$orderTaskFileList['downstream_task_id']}");
            model(VersionModel::class)->commit();

            return $res;
        } catch (Throwable $e) {
            model(VersionModel::class)->rollback();
            throw $e;
        }
    }

    /**
     * 甲方标记已下载
     * @param int $id
     * @param int $userId
     * @param int $tenantId
     * @return array
     * @throws Throwable
     */
    public function markUnzipped(int $id, int $userId, int $tenantId)
    {
        $orderTaskUpstreamVersion = model(OrderTaskUpstreamVersionModel::class)->find($id);
        if (empty($orderTaskUpstreamVersion)) {
            throw new LogicException('Data Invalid', ErrorCode::DATA_INVALID);
        }
        // 通过任务所属项目信息 判断是甲方还是乙方
        $task = model(TaskModel::class)->field('id,project_id')->find($orderTaskUpstreamVersion['task_id']);
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        $project = model(ProjectModel::class)->field('id,type')->find($task['project_id']);
        $isPartyAUnzip = $project['type'] === 'outsource';
        $versionService = VersionService::getInstance();
        $version = $versionService->getVersionById($orderTaskUpstreamVersion['version_id']);
        $relatedOrderListFilter = [
            'from_tenant_id' => $orderTaskUpstreamVersion['from_tenant_id'],
            'to_tenant_id' => $orderTaskUpstreamVersion['to_tenant_id'],
            'task_id' => $orderTaskUpstreamVersion['task_id'],
        ];
        $relatedOrderList = model(OrderTaskFileListModel::class)->where($relatedOrderListFilter)->group('order_id')->field('order_id')->select();
        $relatedOrderIdList = array_column($relatedOrderList, 'order_id');
        $notifyData = [
            'download_role' => '',
            'related_order_id' => $relatedOrderIdList,
        ];
        $checkDownstreamAssembleTaskId = $task['id'];
        if ($isPartyAUnzip) {
            $notifyData['download_role'] = 'partyA';
            // 甲方下载乙方传递的版本数据后
            /**
             * 1. 需要新增版本 (同步新增)
             * 2. 检查是否有相关的订单任务依赖与当前任务(异步检查)
             */
            $orderService = OrderService::getInstance();
            $sourceTask = $orderService->getSourceTaskByTaskId($orderTaskUpstreamVersion['task_id']);
            if (empty($sourceTask)) {
                throw new LogicException('partya already withdraw this task', ErrorCode::PARTYA_ALREADY_WITHDRAW_TASK);
            }
            $toVersion = $version;
            $toVersion['tenant_id'] = $sourceTask['tenant_id'];
            $toVersion['project_id'] = $sourceTask['project_id'];
            $toVersion['link_id'] = $sourceTask['id'];
            $toVersion['param'] = json_decode($version['param'], true);
            $toVersion['json'] = json_decode($version['json'], true);
            unset($toVersion['id'], $toVersion['created'], $toVersion['updated'], $toVersion['created_by']);
            // 检查是否下载过该版本
            $existVersion = model(VersionModel::class)->where(['link_id' => $toVersion['link_id'], 'number' => $toVersion['number']])->find();
            if (!empty($existVersion)) {
                $toVersion['id'] = $existVersion['id'];
            }
            $statusService = StatusService::getInstance();
            $taskDoneStatus = $statusService->getTaskDoneStatus();
            // 甲方下载
            $checkDownstreamAssembleTaskId = $sourceTask['id'];
        } else {
            $notifyData['download_role'] = 'partyB';
            // 乙方下载
        }
        // 检查下游任务待组装
        Client::send('task_downstream_assemble_check', build_queue_data_with_xu(['task_id' => $checkDownstreamAssembleTaskId]));
        $res = [];
        $needPushPartyAVersionAddCheck = false;
        model(TaskModel::class)->startTrans();
        try {
            $res['order_task_upstream_version'] = model(OrderTaskUpstreamVersionModel::class)->modifyItem(['id' => $id, 'is_download' => 'yes', 'is_unzip' => 'yes']);
            if ($isPartyAUnzip) {
                if (isset($toVersion['id']) && $toVersion['id'] > 0) {
                    $res['version'] = model(VersionModel::class)->modifyItem($toVersion);
                } else {
                    $res['version'] = model(VersionModel::class)->addItem($toVersion);
                    $needPushPartyAVersionAddCheck = true;
                }
                // 更新任务状态为已完成
                $res['update_task'] = model(TaskModel::class)->execute("update task set json = json_set(IFNULL(json,'{}'),'$.task_status',{$taskDoneStatus['id']}) where id = {$sourceTask['id']}");
            }
            model(VersionModel::class)->commit();
        } catch (Throwable $e) {
            model(VersionModel::class)->rollback();
            throw $e;
        }
        if (!empty($orderTaskUpstreamVersion['task_id'])) {
            $this->clearDownloading($tenantId, $orderTaskUpstreamVersion['task_id']);
        }
        if ($needPushPartyAVersionAddCheck) {
            Client::send('version_order_task_upstream_check', build_queue_data_with_xu(['version_id' => $res['version']['id']]), 3);
        }
        if (!empty($relatedOrderIdList)) {
            try {
                MessageService::getInstance()->notifyUserEvent($userId, 'order_file_unzipped_success', $notifyData);
            } catch (Throwable $e) {
                trace(format_exception_info($e), 'ERR');
            }
        }
        return $res;
    }

    /**
     * 甲方发布新版本 检查是否需要更新订单的文件依赖版本
     * @param int $versionId
     * @throws \Exception
     */
    public function versionOrderTaskUpstreamCheck(int $versionId)
    {
        $version = model(VersionModel::class)->forceMasterDB()->find($versionId);
        if (empty($version)) {
            throw new LogicException('version not found', ErrorCode::VERSION_NOT_FOUND);
        }
        $downstreamTaskList = TaskUpstreamService::getInstance()->getDownstreamTaskList($version['link_id']);
        $downstreamTaskList = array_filter($downstreamTaskList, function ($item) {
            return in_array($item['executor_type'], TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP);
        });
        if (empty($downstreamTaskList)) {
            return;
        }
        $orderIdList = model(OrderTaskListModel::class)
            ->where(['task_id' => ['IN', array_column($downstreamTaskList, 'id')]])
            ->where(['status' => OrderTaskListModel::STATUS_DEFAULT])
            ->group('order_id')
            ->field('order_id')
            ->select();
        if (empty($orderIdList)) {
            return;
        }

        // 打包了新版本 就查询进行中的订单 关联的传输文件列表 重新添加新版本
        $orderTaskFileListList = model(OrderTaskFileListModel::class)
            ->alias('otfl')
            ->join('`order` o ON o.id = otfl.order_id')
            ->where([
                'o.status' => 'inprogress',
                'otfl.task_id' => $version['link_id'],
            ])
            ->field('otfl.*')
            ->select();

        if (!empty($orderTaskFileListList)) {
            $this->appendOrderTaskFileListVersion($orderTaskFileListList, $version);
        }

        $existOrderIdList = array_keys(array_column($orderTaskFileListList, 'order_id', 'order_id'));
        foreach ($orderIdList as $orderIdItem) {
            if (in_array($orderIdItem['order_id'], $existOrderIdList)) {
                continue;
            }
            Client::send('generate_order_task_file_list', build_queue_data_with_xu(['order_id' => $orderIdItem['order_id'], 'user_id' => $version['created_by']]));
        }
    }

    /**
     * 乙方下载完成 -- 检查关联任务是否需要更新为已就绪
     * @param int $id
     * @return array
     */
    public function orderTaskUpstreamDownloadedCheckTaskStatus(int $id)
    {
        $orderTaskUpstreamVersion = model(OrderTaskUpstreamVersionModel::class)->find($id);
        $notStartStatus = StatusService::getInstance()->getTaskDefaultStatus();
        $waitAssembleStatus = StatusService::getInstance()->getTaskWaitAssembleStatus();
        $task = model(TaskModel::class)->where(['id' => $orderTaskUpstreamVersion['task_id']])->find();
        $downstreamEntityIdList = EntityDownstreamService::getInstance()->getDownstreamEntityList($task['entity_id'], true);
        $entityIdList = $downstreamEntityIdList;
        $entityIdList[] = $task['entity_id'];

        // 查询出和当前任务相关的所有任务 订单状态为进行中
        $taskList = model(OrderTaskFileListModel::class)
            ->alias('otfl')
            ->join('`order` o ON o.id = otfl.order_id')
            ->join('order_task_list on o.id = order_task_list.order_id')
            ->join('task on task.id = order_task_list.real_task_id')
            ->where([
                'o.status' => 'inprogress',
                'otfl.from_tenant_id' => $orderTaskUpstreamVersion['from_tenant_id'],
                'otfl.to_tenant_id' => $orderTaskUpstreamVersion['to_tenant_id'],
                'otfl.task_id' => $orderTaskUpstreamVersion['task_id'],
                'order_task_list.entity_id' => ['IN', $entityIdList],
            ])
            ->group('task.id')
            ->field('task.id,task.json')
            ->select();
        $checkStatus = [$waitAssembleStatus['id'], $notStartStatus['id']];
        foreach ($taskList as $task) {
            $currentTaskJsonFields = json_decode($task['json'], true);
            if (!empty($currentTaskJsonFields['task_status'])
                && !in_array($currentTaskJsonFields['task_status'], $checkStatus)) {
                continue;
            }

            Client::send('task_assemble_check', build_queue_data_with_xu(['task_id' => $task['id']]));
        }

        return $taskList;
    }

    /**
     * 乙方下载完成 异步检查关联的下游任务能否进入就绪状态
     * @param int $id
     */
    public function asyncOrderTaskUpstreamDownloadedCheckTaskStatus(int $id)
    {
        Client::send('order_task_upstream_downloaded_check_task_status', build_queue_data_with_xu(['id' => $id]));
        // $this->orderTaskUpstreamDownloadedCheckTaskStatus()
    }

    /**
     * 任务版本更新 异步检查订单是否需要追加新版本上传下载
     * @param $id
     */
    public function asyncVersionOrderTaskUpstreamCheck($id)
    {
        Client::send('version_order_task_upstream_check', build_queue_data_with_xu(['version_id' => $id]));
        // $this->versionOrderTaskUpstreamCheck()
    }

    /**
     * 通过下游任务id删除文件传输记录
     * @param array $downstreamTaskIdList
     * @return false|float|int|mixed|string
     */
    public function deleteByDownstreamTaskIdList(array $downstreamTaskIdList)
    {
        $orderTaskFileList = model(OrderTaskFileListModel::class)->where(['downstream_task_id' => ['IN', $downstreamTaskIdList]])->select();
        if (empty($orderTaskFileList)) {
            return 0;
        }
        return model(OrderTaskFileListModel::class)->where(['id' => ['IN', array_column($orderTaskFileList, 'id')]])->delete();
    }

    /**
     * 订单上下游依赖 追加新版本
     * @param $orderTaskFileListList
     * @param $version
     * @return void
     */
    private function appendOrderTaskFileListVersion($orderTaskFileListList, $version)
    {
        if (empty($orderTaskFileListList)) {
            return;
        }
        $orderTaskFileListListGroupedByToTenantId = array_group_by($orderTaskFileListList, 'to_tenant_id');
        $orderTaskUpstreamVersionAdd = [];
        foreach ($orderTaskFileListListGroupedByToTenantId as $toTenantId => $orderTaskFileListList) {
            $orderTaskFileListList = array_column($orderTaskFileListList, null, 'task_id');
            // 因为上面查询的时候 使用了task_id 那么这里直接取第一条
            $orderTaskFileList = array_pop($orderTaskFileListList);
            $orderTaskUpstreamVersionFilter = [
                'from_tenant_id' => $orderTaskFileList['from_tenant_id'],
                'to_tenant_id' => $orderTaskFileList['to_tenant_id'],
                'task_id' => $orderTaskFileList['task_id'],
                'version_id' => $version['id'],
            ];
            $orderTaskUpstreamVersion = model(OrderTaskUpstreamVersionModel::class)->where($orderTaskUpstreamVersionFilter)->find();
            if (empty($orderTaskUpstreamVersion)) {
                $orderTaskUpstreamVersionAdd[] = [
                    'from_tenant_id' => $orderTaskFileList['from_tenant_id'],
                    'to_tenant_id' => $orderTaskFileList['to_tenant_id'],
                    'from_project_id' => $orderTaskFileList['from_project_id'],
                    'to_project_id' => $orderTaskFileList['to_project_id'],
                    'entity_id' => $orderTaskFileList['entity_id'],
                    'task_id' => $orderTaskFileList['task_id'],
                    'version_id' => $version['id'],
                ];
            }
        }
        if (empty($orderTaskUpstreamVersionAdd)) {
            return;
        }
        // addAll 经常遇到唯一索引错误 替换为一条一条插入
        foreach ($orderTaskUpstreamVersionAdd as $orderTaskUpstreamVersionItem) {
            try {
                model(OrderTaskUpstreamVersionModel::class)->addItem($orderTaskUpstreamVersionItem);
            } catch (Throwable $e) {
                trace(format_exception_info($e), 'ERR');
            }
        }
    }

    /**
     * 标记下载失败 记录错误信息
     * @param int $taskId
     * @param int $tenantId
     * @return void
     */
    public function markDownloadErr(int $taskId, int $tenantId)
    {
        TenantDownloadUploadQueueService::getInstance()->markInprogressErr($taskId, $tenantId, TenantDownloadUploadQueueService::MARK_DOWNLOAD);
    }

    /**
     * 查询下载中的任务id
     * @param int $tenantId
     * @return array
     */
    public function getDownloadingTaskIdList(int $tenantId)
    {
        return TenantDownloadUploadQueueService::getInstance()->getInprogressList($tenantId, TenantDownloadUploadQueueService::MARK_DOWNLOAD);
    }

    /**
     * 查询项目列表
     *
     * @param int $tenantId
     * @param string $type
     * @return array
     */
    public function getProjectList($tenantId, $type)
    {
        return model(ProjectModel::class)->where(['tenant_id' => $tenantId, 'type' => $type])->select();
    }

    /**
     * 标记上传中
     * @param $taskId
     * @param $extraInfo
     * @param int $tenantId
     * @return bool
     */
    public function markUploading($taskId, $extraInfo, int $tenantId)
    {
        return TenantDownloadUploadQueueService::getInstance()->markInprogress($taskId, $extraInfo, $tenantId, TenantDownloadUploadQueueService::MARK_UPLOAD);
    }

    /**
     * 标牌机上传失败
     * @param $taskId
     * @param int $tenantId
     * @return void
     */
    public function markUploadErr($taskId, int $tenantId)
    {
        TenantDownloadUploadQueueService::getInstance()->markInprogressErr($taskId, $tenantId, TenantDownloadUploadQueueService::MARK_UPLOAD);
    }

    /**
     * 标记已经上传
     * @param array $data
     * @param int $tenantId
     * @return array|bool
     * @throws Exception
     * @throws Throwable
     */
    public function markUploaded(array $data, int $tenantId)
    {
        request()->getModuleCode('version');
        model(VersionModel::class)->startTrans();
        try {
            if ($data['is_uploaded'] === 'yes') {
                $data['uploaded_at'] = $data['uploaded_at'] ?? time();
            }
            $res = CommonService::instance('version')->update($data, 'version');
            TenantDownloadUploadQueueService::getInstance()->removeTaskFromInprogress($res['link_id'], $tenantId, TenantDownloadUploadQueueService::MARK_UPLOAD);
            model(VersionModel::class)->commit();
        } catch (Throwable $e) {
            model(VersionModel::class)->rollback();
            throw $e;
        }
        return $res;
    }

    /**
     * 查询上传中的任务id
     * @param int $tenantId
     * @return array
     */
    private function getUploadingTaskIdList(int $tenantId)
    {
        return TenantDownloadUploadQueueService::getInstance()->getInprogressList($tenantId, TenantDownloadUploadQueueService::MARK_UPLOAD);
    }
}
